<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>漠北烟云</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta property="og:type" content="website">
<meta property="og:title" content="漠北烟云">
<meta property="og:url" content="http://example.com/index.html">
<meta property="og:site_name" content="漠北烟云">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="mobeiyanyun">
<meta name="twitter:card" content="summary">
  
    <link rel="alternate" href="/atom.xml" title="漠北烟云" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/css/style.css">

  
    
<link rel="stylesheet" href="/fancybox/jquery.fancybox.min.css">

  
<meta name="generator" content="Hexo 6.3.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">漠北烟云</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://example.com"></form>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main">
  
    <article id="post-小程序登录设计" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/04/02/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%99%BB%E5%BD%95%E8%AE%BE%E8%AE%A1/" class="article-date">
  <time class="dt-published" datetime="2023-04-02T12:10:07.000Z" itemprop="datePublished">2023-04-02</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/04/02/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%99%BB%E5%BD%95%E8%AE%BE%E8%AE%A1/">小程序登录设计</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p>最近有几个同学同事都问过我这个问题,小程序是如何登录的,如何设计一个小程序账号体系,其实小程序官方已经给出了一个稳妥的方案,我也分享了曾经的方法,今天写些实际的东西,我会结合官方文档详细的介绍一下小程序的登录方法设计,希望可以帮到大家.</p>
<h4 id="小程序的code-session-key-unionId-openId"><a href="#小程序的code-session-key-unionId-openId" class="headerlink" title="小程序的code,session_key,unionId,openId"></a>小程序的code,session_key,unionId,openId</h4><p>开始之前先要对这几个东西又个大概的了解</p>
<ul>
<li>临时登录凭证code,登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程.</li>
<li>session_key 会话密钥</li>
<li>unionid 用户在开放平台的唯一标识符，若当前小程序已绑定到微信开放平台帐号下会返回，</li>
<li>openid 用户唯一标识<br>这里有些朋友不了解opened和unionid的区别, openid相当于是用户在小程序的唯一凭证,unionid则是微信平台的凭证,openid<br>就像我们的学生证或者工号,只是我们在学校或者公司的凭证,而unionId 就像我们的身份证,只要在中国,这个证件都能通用,如果我们要做账户互通,比如公众号,小程序,企业微信账户互通,那么unionid则至关重要.</li>
</ul>
<h4 id="登录方法"><a href="#登录方法" class="headerlink" title="登录方法"></a>登录方法</h4><p>我使用的是这个流程,这个流程并非一成不变根据需求来<br><img decoding="async" src="http://rshobsdhu.hn-bkt.clouddn.com/login.png"><br>页面启动时先使用wx.login去获取微信的code,将code传到后端,后端获取openid,unionid并判断此用户是否在自己的平台上注册过,如果已经注册可直接静默登录,然而大部分页面其实无需登录,根据当前接口判断是否需要注册,如果需要注册就根据接口返回直接进入登录注册页面. wx.login方法详见<a target="_blank" rel="noopener" href="https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html">https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html</a></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">wx.login(&#123;</span><br><span class="line">  success (res) &#123;</span><br><span class="line">    <span class="keyword">if</span> (res.code) &#123;</span><br><span class="line">      //发起网络请求</span><br><span class="line">      wx.request(&#123;</span><br><span class="line">        url: <span class="string">&#x27;https://example.com/onLogin&#x27;</span>,</span><br><span class="line">        data: &#123;</span><br><span class="line">          code: res.code</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      console.log(<span class="string">&#x27;登录失败！&#x27;</span> + res.errMsg)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h4 id="生成token"><a href="#生成token" class="headerlink" title="生成token"></a>生成token</h4><p>如何让后端生成token便是接下来的目标,首先要清楚登录要素是什么,我们的当时是需要手机号,昵称,和unionid,手机号是为了和app数据(app强制绑定手机号的)可以互通,unioid则是为了和公众号互通(方便内容推送),<br>获取手机号详细见<a target="_blank" rel="noopener" href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html">https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html</a><br>获取昵称<a target="_blank" rel="noopener" href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html">https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html</a><br>比较简单不在赘述,需要注意的一点是session_key可能会过期导致手机号无法获取成功,最好获取前checkSession使用检查一下,如果失败了就重新获取session_key还有getPhoneNumber 返回的 code 与 wx.login 返回的 code 作用是不一样的，不能混用。<br>这几大要素齐全了就开始关键的一步:<br><img decoding="async" src="http://rshobsdhu.hn-bkt.clouddn.com/2023040222143.png"></p>
<h4 id="jwt风格的登录"><a href="#jwt风格的登录" class="headerlink" title="jwt风格的登录"></a>jwt风格的登录</h4><p>我们是希望登录时使用jwt风格的登录,所以我封装了几个东西:<br>1、获取token</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">getToken</span></span>() &#123;</span><br><span class="line">     uni.login(&#123;</span><br><span class="line">       provider: <span class="string">&quot;weixin&quot;</span>,</span><br><span class="line">       success: (res) =&gt; &#123;</span><br><span class="line">         const code = res.code;</span><br><span class="line">         getToken(code).<span class="keyword">then</span>((res) =&gt; &#123;</span><br><span class="line">           if (res.data.token) &#123;</span><br><span class="line">             uni.setStorageSync(&quot;token&quot;, res.data.token);</span><br><span class="line">             uni.<span class="variable">$emit</span>(&quot;reload&quot;);</span><br><span class="line">             this.goBack();</span><br><span class="line">           &#125; else &#123;</span><br><span class="line">             this.getSessionKey();</span><br><span class="line">           &#125;</span><br><span class="line">         &#125;);</span><br><span class="line">       &#125;,</span><br><span class="line">     &#125;);</span><br><span class="line">   &#125;,</span><br></pre></td></tr></table></figure>
<p>这一步其实就是用code让后端生成 session_key和openid和unionid.登录页面进入时我调用了这个接口,如果用户之前注册过,短暂的loading就直接生成token并返回,如过之前没有登录过就显示授权手机号的按钮.<br>上一步中已经调用getSessionKey()</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="title">getSessionKey</span></span>() &#123;</span><br><span class="line">   uni.login(&#123;</span><br><span class="line">     provider: <span class="string">&quot;weixin&quot;</span>,</span><br><span class="line">     success: (res) =&gt; &#123;</span><br><span class="line">       const code = res.code;</span><br><span class="line">       getCode(code).<span class="keyword">then</span>((res) =&gt; &#123;</span><br><span class="line">         this.sessionKey = res.data.session_key;</span><br><span class="line">         this.openId = res.data.openId;</span><br><span class="line">         this.unionId = res.data.unionId;</span><br><span class="line">       &#125;);</span><br><span class="line">     &#125;,</span><br><span class="line">   &#125;);</span><br><span class="line"> &#125;,</span><br><span class="line"> getPhoneNumber(e) &#123;</span><br><span class="line">   let _this = this;</span><br><span class="line">   uni.checkSession(&#123;</span><br><span class="line">     success() &#123;</span><br><span class="line">       _this.decryptionPhoneNumber(e);</span><br><span class="line">     &#125;,</span><br><span class="line">     fail() &#123;</span><br><span class="line">       _this.getSessionKey();</span><br><span class="line">       _this.<span class="variable">$toast</span>(&quot;请重试&quot;);</span><br><span class="line">     &#125;,</span><br><span class="line">   &#125;);</span><br><span class="line"> &#125;,</span><br><span class="line"> // 解密手机号</span><br><span class="line"> decryptionPhoneNumber(e) &#123;</span><br><span class="line">		</span><br><span class="line">   const &#123; errMsg, encryptedData, iv &#125; = e.detail;</span><br><span class="line">console.log(&#x27;errMsg&#x27;,errMsg)</span><br><span class="line">   if (errMsg == &quot;getPhoneNumber:ok&quot;) &#123;</span><br><span class="line">     let obj = &#123;</span><br><span class="line">       sessionKey: this.sessionKey,</span><br><span class="line">       encryptedData: encryptedData,</span><br><span class="line">       iv: iv,</span><br><span class="line">       openId: this.openId,</span><br><span class="line">       unionId: this.unionId,</span><br><span class="line">       type: <span class="number">3</span>,</span><br><span class="line">     &#125;;</span><br><span class="line">     wxAuthLogin(obj).then((res) =&gt; &#123;</span><br><span class="line">       uni.setStorageSync(&quot;token&quot;, res.data);</span><br><span class="line">       uni.<span class="variable">$emit</span>(&quot;reload&quot;);</span><br><span class="line">       this.goBack();</span><br><span class="line">     &#125;);</span><br><span class="line">   &#125;</span><br><span class="line">else&#123;</span><br><span class="line"> this.<span class="variable">$toast</span>(&#x27;授权失败&#x27;)</span><br><span class="line">&#125;</span><br><span class="line"> &#125;,</span><br><span class="line"> </span><br><span class="line">  </span><br></pre></td></tr></table></figure>
<p>2、请求封装:<br>我们需要把接口请求进一步的封装</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line">import &#123; baseUrl &#125; from <span class="string">&quot;./getBaseUrl&quot;</span>;// 基础路径</span><br><span class="line">// 错误信息</span><br><span class="line">const tips = &#123;</span><br><span class="line">  1: <span class="string">&quot;抱歉，服务器出现了一个错误&quot;</span>,</span><br><span class="line">&#125;;</span><br><span class="line">class HTTP &#123;</span><br><span class="line">  <span class="function"><span class="title">getPath</span></span>() &#123;</span><br><span class="line">    var pages = getCurrentPages(); // 获取栈实例</span><br><span class="line">    <span class="built_in">let</span> currentRoute = pages[pages.length - 1].route;</span><br><span class="line">    <span class="built_in">return</span> currentRoute;</span><br><span class="line">  &#125;</span><br><span class="line">  request(&#123; url, data = &#123;&#125;, method = <span class="string">&quot;GET&quot;</span>, redirect = <span class="literal">true</span> &#125;) &#123;</span><br><span class="line">    <span class="built_in">return</span> new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">      this._request(url, resolve, reject, data, method, redirect);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  _request(url, resolve, reject, data = &#123;&#125;, method = &quot;GET&quot;, redirect) &#123;</span><br><span class="line">    const token = uni.getStorageSync(&quot;token&quot;);</span><br><span class="line">    uni.request(&#123;</span><br><span class="line">      url: baseUrl + url,</span><br><span class="line">      method: method,</span><br><span class="line">      data: data,</span><br><span class="line">      header: &#123;</span><br><span class="line">        &quot;content-type&quot;: &quot;application/json&quot;,</span><br><span class="line">        token: token,</span><br><span class="line">      &#125;,</span><br><span class="line">      success: (res) =&gt; &#123;</span><br><span class="line">        console.log(res, &quot;res&quot;);</span><br><span class="line">        if (res.data.code === &quot;<span class="number">000000</span>&quot;) &#123;</span><br><span class="line">          resolve(res.data);</span><br><span class="line">        &#125; else if (res.data.code === &quot;<span class="number">200000</span>&quot;||res.data.code === &quot;<span class="number">200006</span>&quot;) &#123;</span><br><span class="line">          reject(res.data);</span><br><span class="line">          const path = this.getPath();</span><br><span class="line">          if (redirect &amp;&amp; path != &quot;pages/wxLogin/index&quot;) &#123;</span><br><span class="line">            console.log(path);</span><br><span class="line">            uni.navigateTo(&#123;</span><br><span class="line">              url: &quot;/pages/wxLogin/index&quot;,</span><br><span class="line">            &#125;);</span><br><span class="line">          &#125;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">          reject(res.data);</span><br><span class="line">          const errorMsg = res.data.message;</span><br><span class="line">          this._show_error(errorMsg);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      fail: (err) =&gt; &#123;</span><br><span class="line">        console.log(&quot;------&quot;);</span><br><span class="line">        reject(err);</span><br><span class="line">        this._show_error(tips[<span class="number">1</span>]);</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  _show_error(errorMsg) &#123;</span><br><span class="line">    uni.showToast(&#123;</span><br><span class="line">      title: errorMsg,</span><br><span class="line">      icon: &quot;none&quot;,</span><br><span class="line">      duration: <span class="number">2000</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">export &#123; HTTP &#125;;</span><br></pre></td></tr></table></figure>
<p>到这里一个小程序的登录流程已经完成.开始基础页面的开发吧<br>官方文档:<a target="_blank" rel="noopener" href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html">https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html</a><br><strong>小程序登录-账户体系设计并不难但是却是一切的开始,千万不要出现这种东西:</strong><br><img decoding="async" src="http://rshobsdhu.hn-bkt.clouddn.com/381660271278_.pic_hd.jpg"></p>
<style>
    blockquote {
        margin:1.6em 0px !important;
        text-align: left !important;
    }
</style>
<blockquote>
看似不起眼的日复一日,会在将来的某一天,突然让你看到坚持的意义.
</blockquote>
      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/04/02/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%99%BB%E5%BD%95%E8%AE%BE%E8%AE%A1/" data-id="clfziy0oq000bp5r46hbua0vi" data-title="小程序登录设计" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F-uniapp/" rel="tag">小程序&#x2F;uniapp</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-观察者模式" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/30/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/" class="article-date">
  <time class="dt-published" datetime="2023-03-30T14:17:58.000Z" itemprop="datePublished">2023-03-30</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/30/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/">vue中的观察者模式</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h3 id="我们面前的山不一定真的难以逾越-甚至不一定真的是山"><a href="#我们面前的山不一定真的难以逾越-甚至不一定真的是山" class="headerlink" title="我们面前的山不一定真的难以逾越,甚至不一定真的是山."></a>我们面前的山不一定真的难以逾越,甚至不一定真的是山.</h3><p>观察者模式（也叫发布&#x2F;订阅模式），它的核心思想是，两块代码不直接通过互相调用来进行通信，而是选择通过事件机制来通信。</p>
<h5 id="观察者模式的特点"><a href="#观察者模式的特点" class="headerlink" title="观察者模式的特点"></a>观察者模式的特点</h5><p>观察者模式的核心优势是事件生产者和事件消费者充分解耦。这样生产者只需要负责触发事件即可，消费者只需要专注处理逻辑即可，它们不需要彼此关心对方的存在和内在逻辑。同时一个事件发生以后也可以由任意多个消费者来进行处理，使得应用的逻辑可以变得更加清晰，避免产生耦合在一起的面条代码。</p>
<p>观察者模式也是有坏处的，即它强行将代码间的关系进行分离，使得开发者在阅读和维护代码的时候不容易一眼看到不同代码之间的逻辑关系。因此对于逻辑关联很强的代码，一般不建议使用观察者模式进行组织。</p>
<p>从代码层面上来说，要实现观察者模式，需要实现三种角色：</p>
<ul>
<li>事件生产者</li>
<li>事件消费者</li>
<li>事件中心<br>事件中心主要是用来管理事件的注册和调用。当需要为某个事件注册一个处理函数的时候，就调用事件中心的“注册”方法，传入事件名称和回调函数，事件中心会记录已经注册的关系。当事件发生时，事件中心会取出与事件相关的回调函数，一一执行，从而完成整个观察者模式的运作。因此事件中心一般至少会暴露.on(event, callback)和.emit(event, data)两个方法，分别用来注册事件处理方法和触发事件，除此之外还可能会提供一个.off(event, callback)方法，用来取消已经注册的事件处理方法</li>
</ul>
<h5 id="浏览器事件机制"><a href="#浏览器事件机制" class="headerlink" title="浏览器事件机制"></a>浏览器事件机制</h5><p>在浏览器环境的编程中，用户发出某些事件（例如点击）的时间点是不确定的，因此当开发者想要响应这些事件时，需要通过addEventListener()注册一个事件处理方法。这便是一个典型的观察者模式的应用。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">document.body.addEventListener(<span class="string">&#x27;click&#x27;</span>, <span class="keyword">function</span>(e)&#123;</span><br><span class="line">    //执行个点击函数</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>浏览器机制有几个特点</p>
<ul>
<li>事件发生时机不确定。</li>
<li>同一个事件可以注册 0 个或者多个处理方法。</li>
<li>事件处理方法之间彼此独立。</li>
<li>事件可以触发 0 次或者多次。<br>这样的机制，就叫作观察者模式，也叫发布&#x2F;订阅模式（pub&#x2F;sub）或者生产者&#x2F;消费者模式。在上面的例子中，浏览器可以叫作事件生产者，而事件处理方法可以叫作事件消费者。</li>
</ul>
<h5 id="Vue-中的观察者模式"><a href="#Vue-中的观察者模式" class="headerlink" title="Vue 中的观察者模式"></a>Vue 中的观察者模式</h5><h6 id="Dep"><a href="#Dep" class="headerlink" title="Dep"></a>Dep</h6><p>Dep是 Vue 对观察者模式的一个封装，用来收集数据之间的依赖关系。</p>
<p>它与普通的观察者模式的区别在于，它只接受Watcher对象作为消费者。它实现了几个方法：<br>addSub() 相当于.on()注册消费者<br>removeSub() 相当于.off()取消注册消费者<br>notify() 相当于.emit()触发事件，只不过消费者都是Watcher类型，所以会直接调用它们的update()方法<br>此外它还有一个depend()方法，是反过来，从Watcher找Dep，最后会通过Watcher调用Dep的addSub()方法完成注册，这个过程有一些绕。</p>
<h6 id="事件机制"><a href="#事件机制" class="headerlink" title="事件机制"></a>事件机制</h6><p>Vue 实例本身就具有$on、$off、$emit、$once等方法，相关代码位于core&#x2F;instances&#x2F;events.js，它通过 Mixin 的机制为 Vue 实例添加了事件相关的方法。因此我们在使用 Vue 的时候就可以直接在实例上调用这些方法，从而很方便地使用观察者模式进行编程</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/30/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/" data-id="clfziy0ot000lp5r44ycpcy04" data-title="vue中的观察者模式" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/VUE/" rel="tag">VUE</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-数据变更的监测" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/29/%E6%95%B0%E6%8D%AE%E5%8F%98%E6%9B%B4%E7%9A%84%E7%9B%91%E6%B5%8B/" class="article-date">
  <time class="dt-published" datetime="2023-03-29T14:19:19.000Z" itemprop="datePublished">2023-03-29</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/29/%E6%95%B0%E6%8D%AE%E5%8F%98%E6%9B%B4%E7%9A%84%E7%9B%91%E6%B5%8B/">数据变更的监测</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p>数据变更的监测方法<br>具体落实到 JavaScript 的技术上时，开发者会发现 JavaScript 在 ES6 之前并没有提供开箱即用的数据变更监测机制。这就使得开发者&#x2F;框架只能自己提供一种可靠的机制来监测数据变更。</p>
<p>JavaScript 曾有过Object.observe()的提案，并且 Chrome 有实现过该方法。该方法会监测数据的变动，然后触发回调。但因为一些原因，后来该提案被取消。</p>
<p>ES6 Proxy<br>ES6 提供了Proxy，顾名思义，它可以为任何一个对象设置一个代理对象，并在代理对象中设置一些自定义的逻辑。这个机制可被用来作为数据变更的监测机制：首先针对需要监听的数据对象设置一个代理对象，在代理对象中拦截对数据赋值的操作，然后直间或间接地进行对应界面的更新。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">const dataObj = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">const dataProxy = new Proxy(dataObj, &#123;</span><br><span class="line">    <span class="built_in">set</span>(target, key, value)&#123;</span><br><span class="line">        // 进行对应界面的更新</span><br><span class="line">        Reflact.<span class="built_in">set</span>(target, key, value);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>相比下面要介绍的几种方法，使用 Proxy 来进行数据变更监测的代码更干净，行为也更可预期。此外，因为Proxy是语言层面提供的机制，JS 引擎在实现的时候也会将这一机制的优化一并考虑在内，因此一般情况下会获得更好的执行性能。</p>
<p>Vue 3.0 将会提供基于Proxy的数据变更监测机制，性能有比较大的提升。当源代码开放后，我们将再专门针对这一块进行解读。</p>
<p>因为只有在 ES6 的环境中才可以使用Proxy，且这个特性无法通过编译到 ES5 的方式进行降级，所以目前兼容性相对来说比较差。目前流行的几大前端框架都需要兼容到 IE9，因此无法享受如此高效便利的数据变更监测机制，而只能八仙过海各显神通。</p>
<p>手工调用（React）<br>既然语言层面没有提供，而开发者又明明白白地知道数据会在什么时候变更，那为什么不让开发者自己来“驱动”一下呢？React 就采用了这样一种简单直白的方案：数据的变更需要开发者调用setState()方法，该方法中就包含了调用界面更新的相关功能。</p>
<p>所以每当应用的状态需要变更的时候，开发者就需要准备好新的数据，然后调用setState()方法将新的数据传递进去，setState()就会将新的数据应用到状态中，然后调用界面渲染的相关逻辑，使用新的状态来渲染界面，保证界面与状态同步。</p>
<p>这种方法从原理上来看十分简单，但是对开发者的编码习惯提出了新的要求，每一次的数据变更都需要手工调方法来更新，而不是大家所熟悉的变量赋值、数组方法等操作。</p>
<p>定时检查&#x2F;脏值检查（Angular.js）<br>Angular.js 同样接受了语言层面没有数据监测机制的事实，但它不希望像 React 一样，界面的更新只能由开发者手工调用，因此它使用了一种看起来更“自动化”的方案——脏值检查。</p>
<p>所谓脏值检查就是通过一些机制来触发检查，通过对比新旧值的方式来确定值是否有变更。</p>
<p>在 Angular.js 1.x 中，这个脏值检查的过程大致如下：</p>
<p>针对需要观察的数据建立一个 watcher 对象，设置isDirty&#x3D;true，并在对象上记录对象的值oldValue<br>在脏值检查被触发的时候，读取新值newValue，如果与oldValue相同，则记isDirty&#x3D;false，否则保持isDirty&#x3D;true不变<br>再次检查，如果isDirty为true，则重写oldValue，此时新值旧值一致，记isDirty&#x3D;false<br>由于：</p>
<p>需要为每一项数据建立 watcher 对象<br>上述检查至少需要执行 2 次，直到新旧值一致才停止<br>而且每次检查对象都是全量数据<br>因此该方法性能不高，这也是 Angular.js 被诟病运行慢的一个重要原因。</p>
<p>getter&#x2F;setter（Vue）<br>上文介绍了Proxy来做数据变更监测的机制，如果你对 ES5 比较熟悉的话，应该很容易想起 ES5 中定义了一套很类似的机制——getter&#x2F;setter。即我们可以针对对象中的某一个成员设置对应的getter&#x2F;setter。</p>
<p>getter&#x2F;setter是两个很特殊的方法，对应对象&#x2F;类实例的一个成员，这个方法并不会被从外部直接调用，而是在对应的成员被访问的时候才会被调用。例如：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">var obj = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">Object.defineProperty(obj, <span class="string">&#x27;foo&#x27;</span>, &#123;</span><br><span class="line">    _value: <span class="string">&#x27;bar&#x27;</span>,</span><br><span class="line">    <span class="function"><span class="title">get</span></span>()&#123;</span><br><span class="line">        <span class="built_in">return</span> this._value;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">set</span>(value)&#123;</span><br><span class="line">        this._value = value;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>当我们访问obj.foo时，get()方法会被调用，因此obj.foo的值就是get()方法的返回值。同理当obj.foo被赋值的时候，set()方法会被调用。</p>
<p>利用这个机制，开发者就很容易知道某个数据正在被赋值，从而获得更新界面的机会。</p>
<p>Vue 就是利用这个机制来实现数据变更的监测的。因此当我们使用 Vue 的时候，只需要重新给某个对象的属性重新赋值，Vue 就会自动帮开发者更新界面，而不需要开发者手工调用任何界面更新的代码。</p>
<p>熟悉 Vue 的读者应该知道，Vue 对数据变更的检测有一些限制：数组中元素重新赋值无法检测到变更；对象中新增加的属性无法检测到变更。这两个限制其实也正是由于getter&#x2F;setter机制本身的限制带来的。在这些情况下，开发者需要显式调用Vue.set或者vm.$set来通知 Vue，相当于回到了 React 的setState()同样的机制，使用起来不太方便。</p>
<p>Proxy机制就不存在类似的问题，因此 Vue 3 选择使用Proxy机制的另一个重要原因就是希望摆脱手工通知数据变更的方式，完全改由框架来监测数据变更，从而带来更一致的开发体验。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/29/%E6%95%B0%E6%8D%AE%E5%8F%98%E6%9B%B4%E7%9A%84%E7%9B%91%E6%B5%8B/" data-id="clfziy0os000gp5r457ex35e5" data-title="数据变更的监测" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/VUE/" rel="tag">VUE</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-闭包" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/26/%E9%97%AD%E5%8C%85/" class="article-date">
  <time class="dt-published" datetime="2023-03-26T14:54:31.835Z" itemprop="datePublished">2023-03-26</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/26/%E9%97%AD%E5%8C%85/">闭包</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h3 id="我曾踏足山巅，也曾进入低谷，二者都让我受益良多"><a href="#我曾踏足山巅，也曾进入低谷，二者都让我受益良多" class="headerlink" title="我曾踏足山巅，也曾进入低谷，二者都让我受益良多"></a>我曾踏足山巅，也曾进入低谷，二者都让我受益良多</h3><p>闭包问题非常常见，被问到的频率也非常高，我也拿这个问过别人，不过大家都不太想听到关于概念背诵，关于百度出来的答案：闭包就是能够读取其他函数内部变量的函数，很准确却也没什么大用。大家更想听到更深入的理解和应用。我认为应该包括的点：作用域、作用域链、变量提升、暂时性死区、执行上下文、词法作用域。<br>说一个简单的代码如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">其实他包括了两个东西编译和执行编译时执行声明var a 在作用域中找 a执行阶段将1赋值给a如过找不到就继续往上找这便形成了所谓的作用域链。目前在javascript中形成了三种作用域：全局作用域、函数作用域、块作用域。</span><br><span class="line"></span><br><span class="line">``` bash</span><br><span class="line">var a = 1; // 全局作用域内的变量</span><br><span class="line">// 函数作用域</span><br><span class="line">function A() &#123;</span><br><span class="line">  var b=2</span><br><span class="line">  console.log(a);</span><br><span class="line">&#125;</span><br><span class="line">// 块作用域</span><br><span class="line">&#123;</span><br><span class="line">    a = 3</span><br><span class="line">&#125;</span><br><span class="line">A(); // 输出 &#x27;3&#x27;</span><br><span class="line">console.log(&quot;b---&quot;,b);//报错 is not defined</span><br></pre></td></tr></table></figure>

<p>变量b由于被放在了 A（）中因此 在全局并读不到他，而在块作用域中a在赋值时向上查找到了已经被声明的并且重新的赋值故而输出了3，到这里就要提下ES6中的概念 let和const</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">var a = 1; // 全局作用域内的变量</span><br><span class="line">// 函数作用域</span><br><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">A</span></span>() &#123;</span><br><span class="line">  var b=2</span><br><span class="line">  console.log(a);</span><br><span class="line">&#125;</span><br><span class="line">// 块作用域</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">let</span> a = 3</span><br><span class="line">&#125;</span><br><span class="line">A(); // 输出 <span class="string">&#x27;1&#x27;</span></span><br><span class="line">console.log(<span class="string">&quot;b---&quot;</span>,b);//报错 is not defined</span><br></pre></td></tr></table></figure>
<p>在使用let时块级作用域下的ab并不会想上查找故而输出1<br>另一个概念是作用域模型，js 遵循的是静态作用域模型，有些地方会提到词法作用域其实指的就是静态作用域，不同的叫法，他们其实指的就是js中最常见的作用域而已。提到这里很多人又会问怎么改变作用域，通常就是 with 和 eval 这两个杀敌一千自损一万的东西。</p>
<p>with:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">var num = &#123;</span><br><span class="line">  a: 1,</span><br><span class="line">  b: 2</span><br><span class="line">&#125;</span><br><span class="line">// with 可以帮我们省去写前缀的时间</span><br><span class="line">with(num) &#123;</span><br><span class="line">  console.log(a)</span><br><span class="line">  console.log(b)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>with 会原地创建一个全新的作用域，这个作用域内的变量集合，其实就是传入 with 的目标对象的属性集合。因为 “创建” 这个动作，是在 with 代码实际已经被执行后发生的，所以这个新作用域确实是在运行时被添加的， with 因此也实现了对书写阶段就划分好的作用域进行修改<br> eval:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> evalfn(num) &#123;</span><br><span class="line">  <span class="built_in">eval</span>(num)</span><br><span class="line">  console.log(a)</span><br><span class="line">&#125;</span><br><span class="line">var a = 1</span><br><span class="line">var b = <span class="string">&#x27;var a = 2&#x27;</span></span><br><span class="line">evalfn(b) //输出 2</span><br></pre></td></tr></table></figure>
<p>不难看出eval其实接受了一个字符串把它当作js代码执行也就时上面的相当于：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> evalfn(num) &#123;</span><br><span class="line">  var a = 2</span><br><span class="line">  console.log(a)</span><br><span class="line">&#125;</span><br><span class="line">var a = 1</span><br><span class="line">evalfn(b) //输出 2</span><br></pre></td></tr></table></figure>
<p>也可以看出这些，要么创建新的作用域或者代人自己本身，都没有真正的改变作用域模型。早就没人用了，而且对性能消耗极大。知道个大概就可以。<br>以上概念知晓后，在去了解闭包才有了意义</p>
<p>1、获取函数内部的局部变量</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">f1</span></span>()&#123;</span><br><span class="line">　　var n=999;</span><br><span class="line">　　　　<span class="keyword">function</span> <span class="function"><span class="title">f2</span></span>()&#123;</span><br><span class="line">　　　　　　<span class="built_in">return</span> n</span><br><span class="line">　　　　&#125;</span><br><span class="line">    <span class="built_in">return</span> f2</span><br><span class="line">&#125;</span><br><span class="line">f1()</span><br></pre></td></tr></table></figure>
<p>在上面的代码中，函数f2就被包括在f1函数内部，这时f1内部的所有局部变量，对f2都是可见的，反之就不行在f1中返回 f2 如此我们便在f1外就访问到了f1内的私有变量。</p>
<p>2、储存变量<br>由于js的垃圾回收机制，变量会被清除，在上面的函数中由于f1是f2的父函数，而f2被赋给了一个全局变量，这导致f2始终在内存中，而f2的存在依赖于f1，因此f1也始终在内存中，不会在调用结束后，被垃圾回收机制（garbage collection）回收。</p>
<p>3、模拟私有变量<br>在JS中其实没有java 中的私有变量的概念，一些私有变量，我们希望它只能在对象内部生效，外部从外部触及，这时我们需要用到闭包了，把私有变量的作用域保护起来</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">var book = (<span class="function"><span class="title">function</span></span>()&#123;</span><br><span class="line">    var page = 100;</span><br><span class="line">    <span class="built_in">return</span> <span class="function"><span class="title">function</span></span>()&#123;</span><br><span class="line">        this.auther = <span class="string">&#x27;dava&#x27;</span>;</span><br><span class="line">        this.price = 200;</span><br><span class="line">        this._page = <span class="function"><span class="title">function</span></span>()&#123;</span><br><span class="line">            <span class="built_in">return</span> page</span><br><span class="line">        &#125;    </span><br><span class="line">    &#125;</span><br><span class="line">&#125;)();</span><br><span class="line">var a = new book();</span><br><span class="line">console.log(a.auther)</span><br><span class="line">console.log(a.price)</span><br><span class="line">console.log(a.page)</span><br><span class="line">console.log(a._page())</span><br></pre></td></tr></table></figure>
<p>这里例子用了一个函数自动执行，一上来就执行了一个匿名函数，并且在匿名函数里面定义了一个局部变量page,然后又返回了一个匿名函数，并且被全局作用域下的book变量接收，此时使用new 调用book就会生成一个新对象a。其中auther属性和price属性可以直接通过对象访问，因为这些属性都是new的时候直接定义在返回的对象身上的,而page属性则没有，因此不能反回，但此时如果我想访问page属性，那就得依靠闭包了，返回的函数在外层的匿名函数里面，因此在返回的函数身上定义了一个方法叫_page，这个方法弹出了page属性，按照js作用域的关系，当前作用域找不到page，就会到上层作用域去寻找，这样就找到了。通过这种方式我们就把私有方法和公有方法区分开了。</p>
<p> 4、偏函数与柯里化<br>柯里化是把接受 n 个参数的 1 个函数改造为只接受 1个参数的 n 个互相嵌套的函数的过程。也就是 fn(a,b,c)fn (a, b, c)fn(a,b,c) 会变成 fn(a)(b)(c)fn (a)(b)(c)fn(a)(b)(c)。且看代码</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> generateName(prefix, <span class="built_in">type</span>, itemName) &#123;</span><br><span class="line">    <span class="built_in">return</span> prefix + <span class="built_in">type</span> + itemName</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>但是有时候业务分化的比较细，可能每个小组只需要传其中的一个参数，但是我们却不得把其余参数在传一次，我们就可以利用闭包存储变量的特性进行改造。成为以下这个样子</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> generateName(prefix) &#123;  </span><br><span class="line">    <span class="built_in">return</span> <span class="keyword">function</span>(<span class="built_in">type</span>) &#123;</span><br><span class="line">        <span class="built_in">return</span> <span class="keyword">function</span> (itemName) &#123;</span><br><span class="line">            <span class="built_in">return</span> prefix + <span class="built_in">type</span> + itemName</span><br><span class="line">        &#125;    </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 生成大卖网商品名专属函数</span><br><span class="line">var salesName = generateName(<span class="string">&#x27;大卖网&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 记住prefix，生成大卖网母婴商品名专属函数</span><br><span class="line">var salesBabyName = salesName(<span class="string">&#x27;母婴&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 记住prefix和<span class="built_in">type</span>，生成洗菜网生鲜商品名专属函数</span><br><span class="line">var vegFreshName = generateName(<span class="string">&#x27;洗菜网&#x27;</span>)(<span class="string">&#x27;生鲜&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 输出 <span class="string">&#x27;大卖网母婴奶瓶&#x27;</span></span><br><span class="line">salesBabyName(<span class="string">&#x27;奶瓶&#x27;</span>)</span><br><span class="line"></span><br><span class="line">// 输出 <span class="string">&#x27;洗菜网生鲜菠菜&#x27;</span></span><br><span class="line">vegFreshName(<span class="string">&#x27;菠菜’)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">// 啥也不记，直接生成一个商品名</span></span><br><span class="line"><span class="string">var itemFullName = generateName(&#x27;</span>洗菜网<span class="string">&#x27;)(&#x27;</span>生鲜<span class="string">&#x27;)(&#x27;</span>菠菜<span class="string">&#x27;)</span></span><br></pre></td></tr></table></figure>
<p>我们看到，在新的 generateName 函数中，我们可以以自由变量的形式将 prefix、type 的值保留在 generateName 内部的两层嵌套的外部作用域里。<br>这样一来，原有的 generateName (prefix, type, name) 现在经过柯里化已经变成了 generateName (prefix)(type)(itemName)。通过后者这种形式，我们可以选择性地决定是否要 “记住” prefix、type，从而即时地生成更加符合我们预期的、复用程度更高的目标函数。此外，柯里化还可以帮助我们以嵌套的形式把多个函数的能力组合到一起，这就是柯里化的魅力。</p>
<p>5、偏函数<br>偏函数和柯里化类似实现方法也类似，直接上代码无需多言</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> generateName(prefix, <span class="built_in">type</span>, itemName) &#123;</span><br><span class="line">    <span class="built_in">return</span> prefix + <span class="built_in">type</span> + itemName</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 调用时一口气传入3个入参</span><br><span class="line">var itemFullName = generateName(<span class="string">&#x27;大卖网&#x27;</span>, <span class="string">&#x27;母婴&#x27;</span>, <span class="string">&#x27;奶瓶&#x27;</span>)</span><br></pre></td></tr></table></figure>
<p>改造下：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> generateName(prefix) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="keyword">function</span>(<span class="built_in">type</span>, itemName) &#123;</span><br><span class="line">        <span class="built_in">return</span> prefix + <span class="built_in">type</span> + itemName</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 把3个参数分两部分传入</span><br><span class="line">var itemFullName = generateName(<span class="string">&#x27;大卖网&#x27;</span>)(<span class="string">&#x27;母婴&#x27;</span>, <span class="string">&#x27;奶瓶&#x27;</span>)</span><br></pre></td></tr></table></figure>
<p>要注意的一些点<br>由于闭包会使得函数中的变量都被保存在内存中，会消耗一定的内存，所以不能滥用闭包，否则会造成网页的性能问题。解决方法是，在退出函数之前，将不使用的局部变量全部删除。<br>闭包会在父函数外部，改变父函数内部变量的值。所以，如果你把父函数当作对象（object）使用，把闭包当作它的公用方法（Public Method），把内部变量当作它的私有属性（private value），这时一定要小心，不要随便改变父函数内部变量的值。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/26/%E9%97%AD%E5%8C%85/" data-id="clfziy0ou000np5r4hp7c2o0o" data-title="闭包" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-instanceof原理" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/21/instanceof%E5%8E%9F%E7%90%86/" class="article-date">
  <time class="dt-published" datetime="2023-03-21T13:55:00.000Z" itemprop="datePublished">2023-03-21</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/21/instanceof%E5%8E%9F%E7%90%86/">instanceof原理</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        
      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/21/instanceof%E5%8E%9F%E7%90%86/" data-id="clfziy0ol0003p5r4dq7l8a6l" data-title="instanceof原理" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-时间复杂度和空间复杂度" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/20/%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E5%92%8C%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6/" class="article-date">
  <time class="dt-published" datetime="2023-03-20T11:54:39.000Z" itemprop="datePublished">2023-03-20</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/20/%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E5%92%8C%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6/">时间复杂度和空间复杂度</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h4 id="什么是复杂度"><a href="#什么是复杂度" class="headerlink" title="什么是复杂度"></a>什么是复杂度</h4><p>复杂度是执行时需要的计算量和内存空间和代码是否简洁无关，他指的是数量级的概念，更多针对具体的算法，而不是完整的系统</p>
<h4 id="时间复杂度-程序执行时需要的计算量"><a href="#时间复杂度-程序执行时需要的计算量" class="headerlink" title="时间复杂度-程序执行时需要的计算量"></a>时间复杂度-程序执行时需要的计算量</h4><p>O(1)一次就够<br>O(n)和传输的数据量一样<br>O(n^2)数据量的平方<br>O(logn)数据量的对数<br>O(n*logn)数据量 * 数据量的对数 </p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"> //O(1)</span><br><span class="line"><span class="keyword">function</span> fn(obj = &#123;&#125;) &#123;</span><br><span class="line">   </span><br><span class="line">    <span class="built_in">return</span> obj.a+obj.b+obj.c</span><br><span class="line">&#125;</span><br><span class="line"> //O(n)</span><br><span class="line"><span class="keyword">function</span> fn(obj = &#123;&#125;) &#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="built_in">let</span> i= 0;i&lt;arr.length;i++)&#123;</span><br><span class="line">    console.log(arr[i])</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"> //O(n^2)</span><br><span class="line"><span class="keyword">function</span> fn(obj = &#123;&#125;) &#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="built_in">let</span> i= 0;i&lt;arr.length;i++)&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="built_in">let</span> j= 0;j&lt;arr.length;i++)&#123;</span><br><span class="line">        console.log(arr[js])</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="空间复杂度-程序执行时需要的内存空间"><a href="#空间复杂度-程序执行时需要的内存空间" class="headerlink" title="空间复杂度-程序执行时需要的内存空间"></a>空间复杂度-程序执行时需要的内存空间</h4>
      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/20/%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E5%92%8C%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6/" data-id="clfziy0ot000ip5r44fq64t5u" data-title="时间复杂度和空间复杂度" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-内存泄露的场景" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/15/%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E7%9A%84%E5%9C%BA%E6%99%AF/" class="article-date">
  <time class="dt-published" datetime="2023-03-15T15:26:31.000Z" itemprop="datePublished">2023-03-15</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/15/%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E7%9A%84%E5%9C%BA%E6%99%AF/">内存泄露的场景</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h5 id="内存泄露的场景"><a href="#内存泄露的场景" class="headerlink" title="内存泄露的场景"></a>内存泄露的场景</h5><ul>
<li>被全局变量、函数引用，组件销毁时未清除。</li>
<li>被全局事件、定时器引用，组件销毁时未清除。</li>
<li>被自定义事件引用，组件销毁时未清除。</li>
</ul>
<h5 id="WeakMap-WeakSet-弱引用"><a href="#WeakMap-WeakSet-弱引用" class="headerlink" title="WeakMap WeakSet 弱引用"></a>WeakMap WeakSet 弱引用</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">const data =&#123;&#125;</span><br><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">fn1</span></span>()&#123;</span><br><span class="line">    const obj =&#123;x:100&#125;</span><br><span class="line">    data.obj = obj</span><br><span class="line">&#125;</span><br><span class="line">fn1()</span><br></pre></td></tr></table></figure>
<p>顶层元素是data，fn1执行后obj一直在被引用着，被标记为可抵达，标记清除无法清理会造成内存泄露</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">const wMap = new WeakMap()</span><br><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">fn1</span></span>()&#123;</span><br><span class="line">   const obj = &#123;x:100&#125;</span><br><span class="line">   wMap.<span class="built_in">set</span>(obj,100)</span><br><span class="line">&#125;</span><br><span class="line">fn1()</span><br></pre></td></tr></table></figure>
<p>WeakMap，WeakSet 是弱引用在fn1执行完毕后引用关系结束，obj被标记为不可抵达，进行垃圾回收</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/15/%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E7%9A%84%E5%9C%BA%E6%99%AF/" data-id="clfziy0on0008p5r49mpp4065" data-title="内存泄露的场景" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-首屏优化" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/14/%E9%A6%96%E5%B1%8F%E4%BC%98%E5%8C%96/" class="article-date">
  <time class="dt-published" datetime="2023-03-14T14:15:31.000Z" itemprop="datePublished">2023-03-14</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/14/%E9%A6%96%E5%B1%8F%E4%BC%98%E5%8C%96/">首屏优化</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h4 id="路由懒加载"><a href="#路由懒加载" class="headerlink" title="路由懒加载"></a>路由懒加载</h4><p>*适用于SPA（不适用MPA）<br>*路由拆分，优先保证首页加载</p>
<h4 id="服务器端渲染SSR"><a href="#服务器端渲染SSR" class="headerlink" title="服务器端渲染SSR"></a>服务器端渲染SSR</h4><ul>
<li>传统的前后端分离（SPA） 渲染页面的过程复杂</li>
<li>SSR渲染页面过程简单，所有性能好</li>
<li>如果是H5页面SSR是性能优化的最好的方案</li>
</ul>
<p>其实SSR是一个非常古老的技术在web1.0时就是SSR如PHP ASP JSP，入行早的同学对这三个应该不陌生，但是随着前端的发展前后端分离成了主流，于是各大框架也推出了SSR方案，Nuxt.js(vue),Next.js(React)</p>
<h4 id="APP预取"><a href="#APP预取" class="headerlink" title="APP预取"></a>APP预取</h4><ul>
<li>如果H5在APP WebView 中展示，可使用APP预取</li>
<li>用户在访问列表页时，APP预加载文章</li>
<li>用户进入H5页，直接从APP中拿内容，相当于本地访问十分快速</li>
</ul>
<h4 id="分页"><a href="#分页" class="headerlink" title="分页"></a>分页</h4><p>这个不多说，但是对于APP 上滑分页，如果碰上列表页中有增删操作容易有页面混乱的问题，处理方案值得单独记一篇</p>
<h4 id="图片懒加载"><a href="#图片懒加载" class="headerlink" title="图片懒加载"></a>图片懒加载</h4><ul>
<li>针对详情页</li>
<li>默认只展示文本，然后触发懒加载</li>
<li>注意提前设置图片尺寸，尽量重绘，不重排</li>
</ul>
<h4 id="Hybrid"><a href="#Hybrid" class="headerlink" title="Hybrid"></a>Hybrid</h4><ul>
<li>提前将HTML JS CSS 下载到APP内部</li>
<li>在APP WebView中使用file：&#x2F;&#x2F;协议加载文件</li>
<li>在用Ajax获取内容</li>
</ul>
<h4 id="实际上的优化"><a href="#实际上的优化" class="headerlink" title="实际上的优化"></a>实际上的优化</h4><p>实际上的优化并不是简单的几个方法，性能优化要结合实际配合分析、统计、评分、查找那个文件拖慢了网页，webpack打包优化，并且还要结合用户体验，loading动画，骨架屏，甚至是利用渐显效果抵消加载的卡顿等等的一些奇迹淫巧，利用这些给出一个完整可行的方案。</p>
<style>
    blockquote {
        margin:1.6em 0px !important;
        text-align: left !important;
    }
</style>
<blockquote>
自负，会让每个人都屈膝下跪。———卡密尔
</blockquote>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/14/%E9%A6%96%E5%B1%8F%E4%BC%98%E5%8C%96/" data-id="clfziy0ov000rp5r412hae95b" data-title="首屏优化" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
</article>



  
    <article id="post-队列和栈" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/14/%E9%98%9F%E5%88%97%E5%92%8C%E6%A0%88/" class="article-date">
  <time class="dt-published" datetime="2023-03-14T13:32:31.000Z" itemprop="datePublished">2023-03-14</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/14/%E9%98%9F%E5%88%97%E5%92%8C%E6%A0%88/">队列和栈</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h5 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h5><p>队列是一种特殊的线性表，特殊之处在于它只允许在表的前端进行删除操作，而在表的后端进行插入操作，和栈一样，队列是一种操作受限制的线性表。进行插入操作的端称为队尾，进行删除操作的端称为队头</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">const queue =[]</span><br><span class="line">queue.push(1) // 入队</span><br><span class="line">queue.push(2)</span><br><span class="line">queue.push(3)</span><br><span class="line"></span><br><span class="line">const n = queue.shift() // 出队</span><br></pre></td></tr></table></figure>
<ul>
<li>队列是逻辑结构，抽象模型</li>
<li>简单的可以用数组、链表实现s</li>
<li>复杂的队列服务，需要单独设计</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/14/%E9%98%9F%E5%88%97%E5%92%8C%E6%A0%88/" data-id="clfziy0ou000pp5r4fcidcqzr" data-title="队列和栈" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/js%E5%9F%BA%E7%A1%80/" rel="tag">js基础</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-异步" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/13/%E5%BC%82%E6%AD%A5/" class="article-date">
  <time class="dt-published" datetime="2023-03-13T14:06:51.000Z" itemprop="datePublished">2023-03-13</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/13/%E5%BC%82%E6%AD%A5/">异步</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p>由于js是单线程的语言，所以在执行时如何全部任由函数同步执行，这就会导致很多问题，页面加载慢，任务阻塞等等，这就要引入异步概念。<br>异步在实现上，依赖一些特殊的语法规则。从整体上来说，异步方案经历了如下的四个进化阶段：<br><strong>回调函数 —&gt; Promise —&gt; Generator —&gt; async&#x2F;await。</strong><br>异步的使用确实带来了很多便利却也带来了很多问题，其中最为严重的就是回调地狱问题，例如B()依赖于 A()就形成</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">A</span></span> () &#123;</span><br><span class="line">    <span class="function"><span class="title">B</span></span>()&#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果再多点</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">A</span></span> () &#123;</span><br><span class="line">   <span class="function"><span class="title">B</span></span>()&#123;</span><br><span class="line">        <span class="function"><span class="title">C</span></span>()&#123;</span><br><span class="line">             <span class="function"><span class="title">D</span></span>()&#123;</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>可读性极差<br>Promise的出现很好的解决了这个问题<br>Promise 会接收一个执行器，在这个执行器里，我们需要把目标的异步任务给”填进去“。<br>在 Promise 实例创建后，执行器里的逻辑会立刻执行，在执行的过程中，根据异步返回的结果，决定如何使用 resolve 或 reject 来改变 Promise实例的状态。 Promise 实例有三种状态：</p>
<ul>
<li>pending状态，表示进行中。这是 Promise 实例创建后的一个初始态；</li>
<li>fulfilled 状态，表示成功完成。这是我们在执行器中调用 resolve 后，达成的状态；</li>
<li>rejected 状态，表示操作失败、被拒绝。这是我们在执行器中调用 reject后，达成的状态；</li>
</ul>
<p>有了promise 就可以通过链式调用解决以上问题</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">A ().<span class="keyword">then</span>(res=&gt;&#123;</span><br><span class="line">    <span class="built_in">return</span> B()</span><br><span class="line">&#125;) .<span class="keyword">then</span>(res=&gt;&#123;</span><br><span class="line">    <span class="built_in">return</span> C()</span><br><span class="line">&#125;).<span class="keyword">then</span>(res=&gt;&#123;</span><br><span class="line">    <span class="built_in">return</span> D()   </span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>除此之外Promise也提供了一些方法：</p>
<h5 id="Promise-all"><a href="#Promise-all" class="headerlink" title="Promise.all:"></a>Promise.all:</h5><p>p1,p2,p3 全部为fulfilled 时返回 fulfilled</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">var p = Promise.all([p1,p2,p3]).<span class="keyword">then</span>(res=&gt;&#123;</span><br><span class="line"></span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<h5 id="Promise-race"><a href="#Promise-race" class="headerlink" title="Promise.race"></a>Promise.race</h5><p>p1,p2,p3 有一个为race时返回 fulfilled</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">var p = Promise.race([p1,p2,p3]).<span class="keyword">then</span>(res=&gt;&#123;&#125;);</span><br></pre></td></tr></table></figure>
<h5 id="Promise-resolve"><a href="#Promise-resolve" class="headerlink" title="Promise.resolve"></a>Promise.resolve</h5><p>有时需要将现有对象转为Promise对象，Promise.resolve方法就起到这个作用。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">var Promise = Promise.resolve($.ajax(<span class="string">&#x27;/whatever.json&#x27;</span>));</span><br></pre></td></tr></table></figure>
<h5 id="Promise-rejected"><a href="#Promise-rejected" class="headerlink" title="Promise.rejected"></a>Promise.rejected</h5><p>Promise.reject(reason)方法也会返回一个新的Promise实例，该实例的状态为rejected。Promise.reject方法的参数reason，会被传递给实例的回调函数。<br>上面代码将 jQuery 生成 deferred 对象，转为一个新的 ES6 的 Promise 对象。<br>如果 Promise.resolve 方法的参数，不是具有 then 方法的对象（又称 thenable 对象），则返回一个新的 Promise 对象，且它的状态为fulfilled</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">var p = Promise.reject(<span class="string">&#x27;出错了&#x27;</span>); p.then(null, <span class="keyword">function</span> (s)&#123; console.log(s) &#125;); // 出错了</span><br></pre></td></tr></table></figure>
<p>async&#x2F;await 和 generator 方案，相较于 Promise 而言，有一个重要的优势：Promise 的错误需要通过回调函数捕获，try catch 是行不通的。而 async&#x2F;await 和 generator 允许 try&#x2F;catch。</p>
<style>
    blockquote {
        margin:1.6em 0px !important;
        text-align: left !important;
    }
</style>
<blockquote>
真正的力量，永远不会属于天生就强壮过人、敏捷过人、聪明过人的家伙，它只会属于愿意不惜一切，达成目的的人。
———希尔科
</blockquote>



      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/13/%E5%BC%82%E6%AD%A5/" data-id="clfziy0or000dp5r4gzad0bku" data-title="异步" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/js%E5%9F%BA%E7%A1%80/" rel="tag">js基础</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-for-await-of" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/12/for-await-of/" class="article-date">
  <time class="dt-published" datetime="2023-03-12T13:52:11.000Z" itemprop="datePublished">2023-03-12</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/12/for-await-of/">for-await-of</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p>for await…of 有什么用？</p>
<p>直接说答案：<br>for await…of 用于遍历多个Promise</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> createPromise（val） &#123;</span><br><span class="line">    <span class="built_in">return</span> new Promise((resolve)=&gt;&#123;</span><br><span class="line">        setTimeout(()=&gt;&#123;</span><br><span class="line">            resolve(val)</span><br><span class="line">        &#125;,<span class="number">1000</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line">(async function()&#123;</span><br><span class="line">    const p1 = createPromise(<span class="number">100</span>)</span><br><span class="line">    const p2 = createPromise(<span class="number">200</span>)</span><br><span class="line">    const p2 = createPromise(<span class="number">300</span>)</span><br><span class="line">    const list = [p1,p2,p3]</span><br><span class="line">    for await (let res of list)&#123;</span><br><span class="line">        console.log(res)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Promise.all(list).then(res=&gt;console.log(res))</span><br><span class="line"></span><br><span class="line">    const res1 = await p1</span><br><span class="line">    const res2 = await p2</span><br><span class="line">    const res3 = await p3</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure>
<p>不难看出 for await of 作用和 promise.all是相同的只是for await of 用循环的形式而promise.all使用的api的方法</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/12/for-await-of/" data-id="clfziy0oh0001p5r4b3x33asj" data-title="for-await-of" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/js%E5%9F%BA%E7%A1%80/" rel="tag">js基础</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-for-in for-of" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/07/for-in%20for-of/" class="article-date">
  <time class="dt-published" datetime="2023-03-07T13:46:53.000Z" itemprop="datePublished">2023-03-07</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/07/for-in%20for-of/">for-in for-of</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h3 id="⼭涧的泉⽔经过⼀曲折，才唱出⼀⽀美妙的歌。"><a href="#⼭涧的泉⽔经过⼀曲折，才唱出⼀⽀美妙的歌。" class="headerlink" title="⼭涧的泉⽔经过⼀曲折，才唱出⼀⽀美妙的歌。"></a>⼭涧的泉⽔经过⼀曲折，才唱出⼀⽀美妙的歌。</h3><h5 id="for-in-和for-of-的区别"><a href="#for-in-和for-of-的区别" class="headerlink" title="for in 和for of 的区别"></a>for in 和for of 的区别</h5><ul>
<li>for in 遍历得到key</li>
<li>for of 遍历得到value</li>
<li>适用于不同的数据类型</li>
<li>遍历对象： for in 可以,for of 不可以</li>
<li>遍历Map Set：for of 可以 for in 不可以</li>
<li>遍历 generator： for of可以，for in 不可以</li>
<li>for in 用于可枚举数据，如对象、数组、字符串</li>
<li>for of用于可迭代数据、如数组、支付串、Map、Set<br>总结</li>
<li>for in 可以用于枚举数据，如对象、数组、字符串、得到Key</li>
<li>for of用于可迭代数据、如数组、字符串、Map、Set得到value</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/07/for-in%20for-of/" data-id="clfziy0om0004p5r40ynmbrc6" data-title="for-in for-of" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/js%E5%9F%BA%E7%A1%80/" rel="tag">js基础</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-对象属性类型问题" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/06/%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7%E7%B1%BB%E5%9E%8B%E9%97%AE%E9%A2%98/" class="article-date">
  <time class="dt-published" datetime="2023-03-06T13:46:59.000Z" itemprop="datePublished">2023-03-06</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/06/%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7%E7%B1%BB%E5%9E%8B%E9%97%AE%E9%A2%98/">对象属性类型问题</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p>这行代码会输出什么呢</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">let</span> a = &#123;&#125;;</span><br><span class="line"><span class="built_in">let</span> b = <span class="string">&#x27;123&#x27;</span>;</span><br><span class="line"><span class="built_in">let</span> c = 123;</span><br><span class="line">a[b] = <span class="string">&#x27;b&#x27;</span>;</span><br><span class="line">a[c] = <span class="string">&#x27;c&#x27;</span>;</span><br><span class="line">console.log(a[b])</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>输出c<br>key 只能接收string和Symbol类型 因此 123 会被转化为‘123’，导致值被覆盖</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">let</span> a = &#123;&#125;;</span><br><span class="line"><span class="built_in">let</span> b = Symblo(<span class="string">&#x27;123&#x27;</span>);</span><br><span class="line"><span class="built_in">let</span> c = Symblo(<span class="string">&#x27;123&#x27;</span>);</span><br><span class="line">a[b] = <span class="string">&#x27;b&#x27;</span>;</span><br><span class="line">a[c] = <span class="string">&#x27;c&#x27;</span>;</span><br><span class="line">console.log(a[b])</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>输出b</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">let</span> a = &#123;&#125;;</span><br><span class="line"><span class="built_in">let</span> b = &#123;key:<span class="string">&#x27;123&#x27;</span>&#125;;</span><br><span class="line"><span class="built_in">let</span> c = &#123;key:<span class="string">&#x27;456&#x27;</span>&#125;;</span><br><span class="line">a[b] = <span class="string">&#x27;b&#x27;</span>;</span><br><span class="line">a[c] = <span class="string">&#x27;c&#x27;</span>;</span><br><span class="line">console.log(a[b])</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>输出 c</p>
<h4 id="js对象Key的数据类型"><a href="#js对象Key的数据类型" class="headerlink" title="js对象Key的数据类型"></a>js对象Key的数据类型</h4><ul>
<li>只能是字符串和Symbol类型</li>
<li>其他类型会被转换成字符串</li>
<li>转换会调用toString()方法</li>
</ul>
<h4 id="Map和WeakMap的Key"><a href="#Map和WeakMap的Key" class="headerlink" title="Map和WeakMap的Key"></a>Map和WeakMap的Key</h4><ul>
<li>map 的key 可以是各种类型</li>
<li>WeakMap的Key只能是引用类型，不能是值类型</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/06/%E5%AF%B9%E8%B1%A1%E5%B1%9E%E6%80%A7%E7%B1%BB%E5%9E%8B%E9%97%AE%E9%A2%98/" data-id="clfziy0oo0009p5r40uh292ec" data-title="对象属性类型问题" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/js%E5%9F%BA%E7%A1%80/" rel="tag">js基础</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-JSBridge" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/05/JSBridge/" class="article-date">
  <time class="dt-published" datetime="2023-03-05T12:53:57.000Z" itemprop="datePublished">2023-03-05</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/05/JSBridge/">JSBridge实现原理</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h3 id="人的烦恼就是记性太好，如果可以把所有事都忘掉，以后每一日都是个新的开始。"><a href="#人的烦恼就是记性太好，如果可以把所有事都忘掉，以后每一日都是个新的开始。" class="headerlink" title="人的烦恼就是记性太好，如果可以把所有事都忘掉，以后每一日都是个新的开始。"></a>人的烦恼就是记性太好，如果可以把所有事都忘掉，以后每一日都是个新的开始。</h3><h5 id="什么是JS-Bridge"><a href="#什么是JS-Bridge" class="headerlink" title="什么是JS Bridge"></a>什么是JS Bridge</h5><p>JSBridge就相当于js与native之间进行通信的一座桥梁，其内部定义了一套用于js与native进行通信的规范（包括协议、方法、传参及回调等）；JSBridge可以桥连js与native的通信，从而使基于容器的web开发和优化成为可能，如比较火的hybrid app技术；能够提升页面性能，丰富页面功能等；</p>
<ul>
<li>js 无法直接调用native  API</li>
<li>需要通过一些特定的格式来调用</li>
<li>这些格式就统称为js-bridge例如 JSSDK</li>
</ul>
<h5 id="实现方式"><a href="#实现方式" class="headerlink" title="实现方式"></a>实现方式</h5><ul>
<li>注册全局API<br>  在全局注册API，事件冒泡到根节点进行监听</li>
<li>URL Scheme<br> h5和native约定一套通信协议作为通信基础，schema:&#x2F;&#x2F;methodName?params&#x3D;xxx&amp;cb&#x3D;xxx;这种方式相当于数据在地址中传输，因此传输长度会有限制</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/05/JSBridge/" data-id="clfziy0oc0000p5r47bydeps2" data-title="JSBridge实现原理" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E6%B7%B7%E5%90%88%E5%BC%80%E5%8F%91/" rel="tag">混合开发</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-vue-nextTick" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/02/vue-nextTick/" class="article-date">
  <time class="dt-published" datetime="2023-03-02T14:18:11.000Z" itemprop="datePublished">2023-03-02</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/02/vue-nextTick/">vue-nextTick</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="弱小和无知从来不是生存的障碍，傲慢才是"><a href="#弱小和无知从来不是生存的障碍，傲慢才是" class="headerlink" title="弱小和无知从来不是生存的障碍，傲慢才是"></a>弱小和无知从来不是生存的障碍，傲慢才是</h2><h5 id="nextTick-到底是个啥"><a href="#nextTick-到底是个啥" class="headerlink" title="nextTick 到底是个啥"></a>nextTick 到底是个啥</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">methods: &#123;</span><br><span class="line">    <span class="function"><span class="title">update</span></span>() &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="built_in">let</span> i = 0; i &lt; 10; i++) &#123;</span><br><span class="line">            this.testNum = this.testNum + i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>

<p>Vue 视图中， testNum 会发生变化。虽然把 firstNum 循环修改了 10 次，但是实际上它只会把最后一次的值更新到视图上——这也是非常合理的。当数据更新发生时，它不会立刻给你执行视图层的更新动作。而是先把这个更新给“存起来”，等到“时机成熟”再执行它；这个“存起来”的地方，叫做异步更新队列；即便一个 watcher 被多次触发，它也只会被推进异步更新队列一次。在同步逻辑执行完之后，watcher 对应的就是其依赖属性的最新的值。最后，Vue 会把异步更新队列的动作集体出队，批量更新。这个实现异步任务派发的接口，就叫做 “nextTick”。</p>
<h5 id="Vue-nextTick-源码"><a href="#Vue-nextTick-源码" class="headerlink" title="Vue-nextTick 源码"></a>Vue-nextTick 源码</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line">import &#123; noop &#125; from <span class="string">&#x27;shared/util&#x27;</span></span><br><span class="line">import &#123; handleError &#125; from <span class="string">&#x27;./error&#x27;</span></span><br><span class="line">import &#123; isIE, isIOS, isNative &#125; from <span class="string">&#x27;./env&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">export</span> <span class="built_in">let</span> isUsingMicroTask = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">const callbacks = []</span><br><span class="line"><span class="built_in">let</span> pending = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">flushCallbacks</span></span> () &#123;</span><br><span class="line">  pending = <span class="literal">false</span></span><br><span class="line">  const copies = callbacks.slice(0)</span><br><span class="line">  callbacks.length = 0</span><br><span class="line">  <span class="keyword">for</span> (<span class="built_in">let</span> i = 0; i &lt; copies.length; i++) &#123;</span><br><span class="line">    copies[i]()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 用来派发异步任务的函数</span><br><span class="line"><span class="built_in">let</span> timerFunc</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 下面这一段逻辑，是根据浏览器的不同，选择不同的 api 来派发异步任务</span><br><span class="line"><span class="keyword">if</span> (typeof Promise !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(Promise)) &#123;</span><br><span class="line">  const p = Promise.resolve()</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    p.then(flushCallbacks)</span><br><span class="line">    <span class="keyword">if</span> (isIOS) setTimeout(noop)</span><br><span class="line">  &#125;</span><br><span class="line">  isUsingMicroTask = <span class="literal">true</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (!isIE &amp;&amp; typeof MutationObserver !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; (</span><br><span class="line">  isNative(MutationObserver) ||</span><br><span class="line">  MutationObserver.toString() === <span class="string">&#x27;[object MutationObserverConstructor]&#x27;</span></span><br><span class="line">)) &#123;</span><br><span class="line">  <span class="built_in">let</span> counter = 1</span><br><span class="line">  const observer = new MutationObserver(flushCallbacks)</span><br><span class="line">  const textNode = document.createTextNode(String(counter))</span><br><span class="line">  observer.observe(textNode, &#123;</span><br><span class="line">    characterData: <span class="literal">true</span></span><br><span class="line">  &#125;)</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    counter = (counter + 1) % 2</span><br><span class="line">    textNode.data = String(counter)</span><br><span class="line">  &#125;</span><br><span class="line">  isUsingMicroTask = <span class="literal">true</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (typeof setImmediate !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(setImmediate)) &#123;</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    setImmediate(flushCallbacks)</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  // Fallback to setTimeout.</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    setTimeout(flushCallbacks, 0)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 暴露 nextTick 方法</span><br><span class="line"><span class="built_in">export</span> <span class="keyword">function</span> nextTick (cb?: Function, ctx?: Object) &#123;</span><br><span class="line">  <span class="built_in">let</span> _resolve</span><br><span class="line">  // 维护一个异步更新队列</span><br><span class="line">  callbacks.push(() =&gt; &#123;</span><br><span class="line">    if (cb) &#123;</span><br><span class="line">      try &#123;</span><br><span class="line">        cb.call(ctx)</span><br><span class="line">      &#125; catch (e) &#123;</span><br><span class="line">        handleError(e, ctx, &#x27;nextTick&#x27;)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (_resolve) &#123;</span><br><span class="line">      _resolve(ctx)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">  // pending 是一个锁，确保任务执行的有序性</span><br><span class="line">  if (!pending) &#123;</span><br><span class="line">    pending = true</span><br><span class="line">    timerFunc()</span><br><span class="line">  &#125;</span><br><span class="line">  // 兜底逻辑，处理入参不是回调的情况</span><br><span class="line">  if (!cb &amp;&amp; typeof Promise !== &#x27;undefined&#x27;) &#123;</span><br><span class="line">    return new Promise(resolve =&gt; &#123;</span><br><span class="line">      _resolve = resolve</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面这段代码里，有三个关键角色：nextTick、timerFunc 和 flushCallbacks，nextTick 是入口函数</p>
<h5 id="逻辑统筹者——nextTick"><a href="#逻辑统筹者——nextTick" class="headerlink" title="逻辑统筹者——nextTick"></a>逻辑统筹者——nextTick</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">// 暴露 nextTick 方法</span><br><span class="line"><span class="built_in">export</span> <span class="keyword">function</span> nextTick (cb?: Function, ctx?: Object) &#123;</span><br><span class="line">  <span class="built_in">let</span> _resolve</span><br><span class="line">  // 维护一个异步更新队列</span><br><span class="line">  callbacks.push(() =&gt; &#123;</span><br><span class="line">    if (cb) &#123;</span><br><span class="line">      try &#123;</span><br><span class="line">        cb.call(ctx)</span><br><span class="line">      &#125; catch (e) &#123;</span><br><span class="line">        handleError(e, ctx, &#x27;nextTick&#x27;)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (_resolve) &#123;</span><br><span class="line">      _resolve(ctx)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">  // pending 是一个锁，确保任务执行有序、不重复</span><br><span class="line">  if (!pending) &#123;</span><br><span class="line">    pending = true</span><br><span class="line">    timerFunc()</span><br><span class="line">  &#125;</span><br><span class="line">  // 兜底逻辑，处理入参不是回调的情况</span><br><span class="line">  if (!cb &amp;&amp; typeof Promise !== &#x27;undefined&#x27;) &#123;</span><br><span class="line">    return new Promise(resolve =&gt; &#123;</span><br><span class="line">      _resolve = resolve</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>有三个变量需要引起重视</p>
<ul>
<li>callbacks - 异步更新队列</li>
<li>pending - “锁”</li>
<li>timerFunc - 异步任务的派发函数</li>
</ul>
<p>首先，<strong>nextTick 的入参是一个回调函数，这个回调函数就是一个“任务”。</strong>每次 nextTick 接收一个任务，它不会立刻去执行它，而是把它 push 进 callbacks 这个异步更新队列里（也就是存起来）。接着，去检查 pending 的值。这个 pending 有何妙用呢？大家知道，我这个异步更新队列肯定不能一直往里塞东西，我得找个时机把它派发出去对不对？那么如何决定啥时候派发它呢？</p>
<p>如果说这个 pending 为 false，意味着啥？ 意味着“现在还没有一个异步更新队列被派发出去”，那么就调用 timerFunc，把当前维护的这个异步队列给派发出去；那如果 pending 为 true 呢？意味着现在异步更新队列（callbacks）已经被派发出去了，此时 callbacks 已经呆在浏览器的异步任务队列里、确保会被执行了，因此没有必要再执行一遍 timerFunc 去重复派发这个队列，只需要往里面添加任务就可以了。</p>
<h5 id="异步任务派发器——timerFunc"><a href="#异步任务派发器——timerFunc" class="headerlink" title="异步任务派发器——timerFunc"></a>异步任务派发器——timerFunc</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">// 用来派发异步任务的函数</span><br><span class="line"><span class="built_in">let</span> timerFunc</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 下面这一段逻辑，是根据浏览器的不同，选择不同的 api 来派发异步任务</span><br><span class="line"><span class="keyword">if</span> (typeof Promise !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(Promise)) &#123;</span><br><span class="line">  const p = Promise.resolve()</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    p.then(flushCallbacks)</span><br><span class="line">    <span class="keyword">if</span> (isIOS) setTimeout(noop)</span><br><span class="line">  &#125;</span><br><span class="line">  isUsingMicroTask = <span class="literal">true</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (!isIE &amp;&amp; typeof MutationObserver !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; (</span><br><span class="line">  isNative(MutationObserver) ||</span><br><span class="line">  MutationObserver.toString() === <span class="string">&#x27;[object MutationObserverConstructor]&#x27;</span></span><br><span class="line">)) &#123;</span><br><span class="line">  <span class="built_in">let</span> counter = 1</span><br><span class="line">  const observer = new MutationObserver(flushCallbacks)</span><br><span class="line">  const textNode = document.createTextNode(String(counter))</span><br><span class="line">  observer.observe(textNode, &#123;</span><br><span class="line">    characterData: <span class="literal">true</span></span><br><span class="line">  &#125;)</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    counter = (counter + 1) % 2</span><br><span class="line">    textNode.data = String(counter)</span><br><span class="line">  &#125;</span><br><span class="line">  isUsingMicroTask = <span class="literal">true</span></span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (typeof setImmediate !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(setImmediate)) &#123;</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    setImmediate(flushCallbacks)</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  // Fallback to setTimeout.</span><br><span class="line">  timerFunc = () =&gt; &#123;</span><br><span class="line">    setTimeout(flushCallbacks, 0)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>关键的点</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">timerFunc = () =&gt; &#123;</span><br><span class="line">  p.then(flushCallbacks)</span><br><span class="line">  <span class="keyword">if</span> (isIOS) setTimeout(noop)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">timerFunc = () =&gt; &#123;</span><br><span class="line">  counter = (counter + 1) % 2</span><br><span class="line">  textNode.data = String(counter)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">timerFunc = () =&gt; &#123;</span><br><span class="line">  setImmediate(flushCallbacks)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">timerFunc = () =&gt; &#123;</span><br><span class="line">  setTimeout(flushCallbacks, 0)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>你会发现，不同的 timerFunc 之间有一个共性——它们都在派发 flushCallbacks 这个函数（这个函数我们下面会讲）。那么不同的 timerFunc 间有啥区别呢？我们看到区别在于派发 flushCallbacks 的方式不同，这里一共有四种派发方式：</p>
<ul>
<li>Promise.then</li>
<li>MutationObserver</li>
<li>setImmediate</li>
<li>setTimeout<br>除去处理浏览器的和一些细节其实事情很简单：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(当前环境支持 Promise)&#123;</span><br><span class="line">    Promise.<span class="keyword">then</span> 派发 timerFunc</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (当前环境支持 MutationObserver) &#123;</span><br><span class="line">        MutationObserver 派发 timerFunc</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (当前环境支持 setImmediate) &#123;</span><br><span class="line">    setImmediate 派发 timerFunc</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    setTimeout 派发 timer Func</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
我们看到，timerFunc 按照优先级分别可能通过：Promise.then、MutationObserver、setImmediate、setTimeout 四种途径派发。这个优先级比较有看头，大家会发现它是优先派发 micro-task、次选 macro-task。</li>
</ul>
<h5 id="为什么-Vue-优先派发的是-micro-task？"><a href="#为什么-Vue-优先派发的是-micro-task？" class="headerlink" title="为什么 Vue 优先派发的是 micro-task？"></a>为什么 Vue 优先派发的是 micro-task？</h5><p>Event-Loop 的执行流程</p>
<ol>
<li><p>执行并出队一个 macro-task。</p>
</li>
<li><p>全局上下文（script 标签）被推入调用栈，同步代码执行。在执行的过程中，通过对一些接口的调用，可以产生新的 macro-task 与 micro-task，它们会分别被推入各自的任务队列里。这个过程本质上是队列的 macro-task 的执行和出队的过程。</p>
</li>
<li><p>上一步我们出队的是一个 macro-task，这一步我们处理的是 micro-task。但需要注意的是：当 macro-task 出队时，任务是一个一个执行的；而 micro-task 出队时，任务是一队一队执行的。因此，我们处理 micro 队列这一步，会逐个执行队列中的任务并把它出队，直到队列被清空。</p>
</li>
<li><p>执行渲染操作，更新界面；</p>
</li>
<li><p>检查是否存在 Web worker 任务，如果有，则对其进行处理；</p>
</li>
</ol>
<p>现在需要大家特别关注的是 1、2、3、4 之间的关系！大家想，如果我在 2 中派发的是一个 macro-task，那么这个任务会在什么时候被执行？</p>
<p>仔细想想，它是不是会被推移到下一个循环里的 1 中被执行？这就有问题了——如果我的任务是用来更新 UI 界面的，那么它在我当前循环的 4 中并不会被感知；你只能等它在下一个循环中的 1 中生效后，才能在下一个循环的 4 中被感知、进而更新到界面上。</p>
<p>问题出现了：macro-task 形式的派发，会导致我们的界面更新延迟一个事件循环。在当前循环的 4，这个渲染时机一定程度上被“浪费”了，它并不能及时渲染出我们的更新。</p>
<p>此外，micro-task 是一队一队来更新，而 macro-task 是一个一个来更新。从更新效率上来说，micro-task 也会更优秀。</p>
<h5 id="任务执行器——flushCallbacks"><a href="#任务执行器——flushCallbacks" class="headerlink" title="任务执行器——flushCallbacks"></a>任务执行器——flushCallbacks</h5><p>flushCallbacks 的逻辑相对上面简单不少：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">flushCallbacks</span></span> () &#123;</span><br><span class="line">  // 把“锁”打开</span><br><span class="line">  pending = <span class="literal">false</span></span><br><span class="line">  // 创造 callbacks 副本，避免副作用</span><br><span class="line">  const copies = callbacks.slice(0)</span><br><span class="line">  // callbacks 队列置空</span><br><span class="line">  callbacks.length = 0</span><br><span class="line">  // 逐个执行异步任务</span><br><span class="line">  <span class="keyword">for</span> (<span class="built_in">let</span> i = 0; i &lt; copies.length; i++) &#123;</span><br><span class="line">    copies[i]()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>它负责把 callbacks 里的任务逐个取出，依次执行。</p>
<p>注意，进入 flushCallbacks 后，做的第一件事情就是把 pending 置为 false。因为 flushCallbacks 执行完毕后，callbacks 将被清空、浏览器的异步任务队列中也就没有 Vue 的异步任务了。此时必须把 pending 置空，确保下一个 Vue 异步任务队列进来时，可以及时被派发。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/02/vue-nextTick/" data-id="clfziy0om0005p5r412st6fxh" data-title="vue-nextTick" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/VUE/" rel="tag">VUE</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-跨域解决方案" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/2023/03/02/%E8%B7%A8%E5%9F%9F%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" class="article-date">
  <time class="dt-published" datetime="2023-03-01T16:03:45.000Z" itemprop="datePublished">2023-03-02</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/2023/03/02/%E8%B7%A8%E5%9F%9F%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/">跨域解决方案</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="只有弱者才会恐惧阴影，于是恐惧便压倒了他们。"><a href="#只有弱者才会恐惧阴影，于是恐惧便压倒了他们。" class="headerlink" title="只有弱者才会恐惧阴影，于是恐惧便压倒了他们。"></a>只有弱者才会恐惧阴影，于是恐惧便压倒了他们。</h2><h5 id="什么是跨域"><a href="#什么是跨域" class="headerlink" title="什么是跨域?"></a>什么是跨域?</h5><p>同源策略:这里的源(origin)指的是协议、域名、端口号，同源指的是在url中协议、域名、端口号均相同。那么同源策略是浏览器的一个安全功能，不同源的脚本在没有明确授权的情况下，不能读写对方资源。</p>
<p>注意这个“不能读写资源”的含义，它主要限制了以下三个方面：</p>
<p>Cookie、LocalStorage 和 IndexDB 无法读取<br>DOM 和 JS 对象无法获取<br>Ajax请求发送不出去<br>只要协议、域名、端口有任何一个不同，都被当作是不同的域，这就是所谓“跨域”。</p>
<p>虽然同源策略带来了安全上的保证，但是实际业务中，跨域的场景实在是太多了。如果仅仅因为跨域就导致资源无法互相读写，那么我们现在看到的许多互联网功能都将原地歇菜。之所以没歇菜，是因为网络策略有其灵活性，我们可以通过一些方式来绕过同源策略、达到通信目的。</p>
<h5 id="跨域解决方案"><a href="#跨域解决方案" class="headerlink" title="跨域解决方案"></a>跨域解决方案</h5><h6 id="1-JSONP"><a href="#1-JSONP" class="headerlink" title="1) JSONP:"></a>1) JSONP:</h6><p>由于js调用跨域文件是被允许的。只要我们在远程服务器上设法把数据装进js格式的文件里，就可以供客户端调用和进一步处理。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">    &lt;body&gt;</span><br><span class="line">        &lt;script <span class="built_in">type</span>=<span class="string">&quot;text/javascript&quot;</span>&gt;</span><br><span class="line">            // callback是存在服务端的</span><br><span class="line">            <span class="keyword">function</span> callback(data) &#123;</span><br><span class="line">                console.log(data)</span><br><span class="line">            &#125;</span><br><span class="line">        &lt;/script&gt;</span><br><span class="line">        // 这里引入服务端代码</span><br><span class="line">        &lt;script src=<span class="string">&quot;http://www.xxxx.com/jsonp&quot;</span> <span class="built_in">type</span>=<span class="string">&quot;text/javascript&quot;</span> charset=<span class="string">&quot;utf-8&quot;</span>&gt;&lt;/script&gt;</span><br><span class="line">    &lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure>
<h6 id="2-CORS"><a href="#2-CORS" class="headerlink" title="2) CORS:"></a>2) CORS:</h6><p>CORS是一个W3C标准，全称是”跨域资源共享”（Cross-origin resource sharing）。</p>
<p>它允许浏览器向不同源的服务器，发出XMLHttpRequest请求。虽然需要浏览器和服务器同时支持，但目前来看，除了低版本 IE 外，基本所有浏览器都支持该功能。</p>
<p>CORS的通信过程，实际上不需要什么代码层面的配合与改动，由浏览器自动实现。</p>
<p>对于开发者来说，CORS通信与同源的通信没有差别，至少代码上是一样的。浏览器一旦发现AJAX请求跨域，就会自动添加一些附加的头信息、追加必要的请求，但用户不会有感觉。</p>
<p>浏览器的行为是通用的、自动化的。因此能否实现 CORS 的关键，其实在于服务器是否对此提供支持。我们下面从过程来理解一下浏览器和服务器在 CORS 上的合作机制：</p>
<p>简单请求对应的 CORS 行为</p>
<p>浏览器会把请求分为简单请求和非简单请求，对于这两种请求，CORS 的处理过程是不同的，我们先来看简单请求：</p>
<p>请求方式为HEAD、POST 或者 GET<br>http头信息不超出以下字段：Accept、Accept-Language 、 Content-Language、 Last-Event-ID、 Content-Type(限于三个值：application&#x2F;x-www-form-urlencoded、multipart&#x2F;form-data、text&#x2F;plain)</p>
<p>满足这两个条件的，就是简单请求。对于简单请求，对于简单请求，浏览器直接发出CORS请求。具体来说，就是在头信息之中，增加一个Origin字段：</p>
<p>Origin: <a target="_blank" rel="noopener" href="http://baidu.com/">http://baidu.com</a><br>Origin字段用来说明，本次请求来自哪个源（协议 + 域名 + 端口）。服务器根据这个值，决定是否同意这次请求。服务器处理的结果，分为两种情况：</p>
<p>不同意： 如果Origin指定的源，不在许可范围内，服务器会返回一个正常的HTTP回应；浏览器发现，这个回应的头信息没有包含Access-Control-Allow-Origin字段，就知道出错了，从而抛出一个错误，被 XMLHttpRequest 的 onerror 回调函数捕获。<br>同意：如果Origin指定的域名在许可范围内，服务器返回的响应，会多出这个关键的头信息字段：<br>Access-Control-Allow-Origin: <a target="_blank" rel="noopener" href="http://baidu.com/">http://baidu.com</a><br>这个字段用于说明服务器接纳哪些域名。它的值要么是请求时Origin字段的值，要么是一个*——表示接受任意域名的请求。</p>
<p>复杂请求对应的 CORS 行为<br>有一些请求对服务器有着特殊的要求，比如请求方法是PUT或DELETE，或者Content-Type字段的类型是application&#x2F;json。</p>
<p>非简单请求的CORS请求，会在正式通信之前，增加一次HTTP查询请求，称为”预检”请求（preflight）。</p>
<p>这个 preflight 的作用在于，确认当前网页所在的域名是否在服务器的许可名单之中、明确可以使用的 HTTP 请求方法和头信息字段。只有在这个请求返回成功的情况下，浏览器才会发出正式的请求。</p>
<p>这样做的目的是为了避免“无用功”。要知道，一般来说，正式请求要携带一些信息，它体积可能比较大。如果我们背着这么大一个包袱到了服务端那边，却发现对方根本不接受你，那岂不是白费力气了嘛。所以说，发送正式请求前先“预检”，就跟结婚之前要先订婚一样，是一个必要的确认动作。</p>
<p>CORS 和 JSONP 的对比<br>CORS 的优势，往往是相对于 JSONP 来说的：JSONP只支持GET请求，而CORS支持所有类型的HTTP请求。但相应地，JSONP在低版本 IE 上也可以畅通无阻，CORS 就没有这么好的兼容性了。</p>
<h6 id="3-postMessage跨域"><a href="#3-postMessage跨域" class="headerlink" title="3) postMessage跨域"></a>3) postMessage跨域</h6><p>这个 API 从 H5 开始支持，通过注册监听信息的Message事件、调用发送信息的postMessage方法，我们可以实现跨窗口通信。</p>
<p>从广义上讲，一个窗口可以获得对另一个窗口的引用（比如 targetWindow &#x3D; window.opener），然后在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息。接收消息的窗口可以根据需要自由处理此事件。传递给 window.postMessage() 的参数（比如 message ）将通过消息事件对象暴露给接收消息的窗口。</p>
<p>发送信息的postMessage方法<br>otherWindow.postMessage(message, targetOrigin, [transfer]);<br>这里otherWindow是对目标窗口的引用；message是要发送的消息；targetOrigin是限定消息接受范围，一般是字符串或者URI，星号 * 则意味着不限制。</p>
<p>接受信息的message事件</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">var onmessage = <span class="keyword">function</span>(event) &#123;</span><br><span class="line">  var data = event.data;</span><br><span class="line">  var origin = event.origin;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span>(typeof window.addEventListener != <span class="string">&#x27;undefined&#x27;</span>)&#123;</span><br><span class="line">    window.addEventListener(<span class="string">&#x27;message&#x27;</span>,onmessage,<span class="literal">false</span>);</span><br><span class="line">&#125;<span class="keyword">else</span> <span class="keyword">if</span>(typeof window.attachEvent != <span class="string">&#x27;undefined&#x27;</span>)&#123;</span><br><span class="line">    window.attachEvent(<span class="string">&#x27;onmessage&#x27;</span>, onmessage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>流程演示<br>下面我们通过一个例子来理解这个过程：<br>a页面中对消息接受和派发的处理：<br>a页面</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&lt;iframe <span class="built_in">id</span>=<span class="string">&quot;iframe&quot;</span> src=<span class="string">&quot;http://www.xxxx/b.html&quot;</span> style=<span class="string">&quot;display:none;&quot;</span>&gt;&lt;/iframe&gt;</span><br><span class="line">&lt;script&gt;       </span><br><span class="line">    var iframe = document.getElementById(<span class="string">&#x27;iframe&#x27;</span>);</span><br><span class="line">    iframe.onload = <span class="function"><span class="title">function</span></span>() &#123;</span><br><span class="line">        var data = &#123;</span><br><span class="line">            name: <span class="string">&#x27;xiuyan&#x27;</span></span><br><span class="line">        &#125;;</span><br><span class="line">        // a 页面向 b 页面派发消息</span><br><span class="line">        iframe.contentWindow.postMessage(JSON.stringify(data), <span class="string">&#x27;http://www.neal.cn&#x27;</span>);</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    // a 页面接受 b 页面的消息</span><br><span class="line">   window.addEventListener(<span class="string">&quot;message&quot;</span>, <span class="keyword">function</span>( event ) &#123;</span><br><span class="line">      console.log(<span class="string">&#x27;data from b is&#x27;</span>, event.data)</span><br><span class="line">   &#125;);</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure>
<p>b页面对消息接受和派发的处理：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&lt;script&gt;</span><br><span class="line">    // 接收 a 页面的数据</span><br><span class="line">    window.addEventListener(<span class="string">&#x27;message&#x27;</span>, <span class="keyword">function</span>(e) &#123;</span><br><span class="line">      console.log(<span class="string">&#x27;data from a is&#x27;</span>, event.data)</span><br><span class="line"></span><br><span class="line">        var data = JSON.parse(e.data);</span><br><span class="line">        <span class="keyword">if</span> (data) &#123;</span><br><span class="line">            data.age = 100;</span><br><span class="line"></span><br><span class="line">            // 派发数据到 a 页面</span><br><span class="line">            window.parent.postMessage(JSON.stringify(data), <span class="string">&#x27;http://www.xxxx.com&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="literal">false</span>);</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure>
      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2023/03/02/%E8%B7%A8%E5%9F%9F%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" data-id="clfziy0ow000tp5r443ljbpig" data-title="跨域解决方案" class="article-share-link">Share</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/js%E5%9F%BA%E7%A1%80/" rel="tag">js基础</a></li></ul>

    </footer>
  </div>
  
</article>



  


</section>
        
          <aside id="sidebar">
  
    

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/tags/VUE/" rel="tag">VUE</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/js%E5%9F%BA%E7%A1%80/" rel="tag">js基础</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F-uniapp/" rel="tag">小程序&#x2F;uniapp</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%B7%B7%E5%90%88%E5%BC%80%E5%8F%91/" rel="tag">混合开发</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/tags/VUE/" style="font-size: 15px;">VUE</a> <a href="/tags/js%E5%9F%BA%E7%A1%80/" style="font-size: 20px;">js基础</a> <a href="/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F-uniapp/" style="font-size: 10px;">小程序/uniapp</a> <a href="/tags/%E6%B7%B7%E5%90%88%E5%BC%80%E5%8F%91/" style="font-size: 10px;">混合开发</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2023/04/">April 2023</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2023/03/">March 2023</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2023/04/02/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E7%99%BB%E5%BD%95%E8%AE%BE%E8%AE%A1/">小程序登录设计</a>
          </li>
        
          <li>
            <a href="/2023/03/30/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/">vue中的观察者模式</a>
          </li>
        
          <li>
            <a href="/2023/03/29/%E6%95%B0%E6%8D%AE%E5%8F%98%E6%9B%B4%E7%9A%84%E7%9B%91%E6%B5%8B/">数据变更的监测</a>
          </li>
        
          <li>
            <a href="/2023/03/26/%E9%97%AD%E5%8C%85/">闭包</a>
          </li>
        
          <li>
            <a href="/2023/03/21/instanceof%E5%8E%9F%E7%90%86/">instanceof原理</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      
      &copy; 2023 mobeiyanyun<br>
      Powered by <a href="https://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>

    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="/js/jquery-3.4.1.min.js"></script>



  
<script src="/fancybox/jquery.fancybox.min.js"></script>




<script src="/js/script.js"></script>





  </div>
</body>
</html>